package com.qfmy.aop;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import model.annotation.BreezeRedisCacheAnnotation;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author 清风明月
 * @Package com.qfmy.aop
 * @date 2025/6/26
 * @description 自定义缓存查询组件
 */
@Slf4j
@Aspect
@Component
@SuppressWarnings("all")
public class BreezeRedisCache {

    /**
     * 缓存查询切点
     */
    @Pointcut("@annotation(model.annotation.BreezeRedisCache)")
    public void cache() {
    }

    /**
     * 注入redistemplate
     */
    @Resource
    private RedisTemplate redisTemplate;

    @Around("cache()")
    public Object around(@NotNull ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取方法的注解
        BreezeRedisCacheAnnotation annotation = method.getAnnotation(BreezeRedisCacheAnnotation.class);
        if (annotation == null) {
            return joinPoint.proceed(); // 没有注解则直接执行方法
        }

        // 获取注解参数
        long expire = annotation.expire();
        TimeUnit timeUnit = annotation.timeUnit();
        boolean isCacheNull = annotation.isCacheNull();
        String placeholder = annotation.placeholder();
        String keyPrefix = annotation.KeyPrefix();

        // 创建SpringEl表达式对象
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(placeholder);
        StandardEvaluationContext context = new StandardEvaluationContext();

        // 获取参数
        Object[] args = joinPoint.getArgs();

        // 获取参数名称
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);

        if (parameterNames != null) {
            for (int i = 0; i < args.length; i++) {
                // 设置参数到SpEL上下文
                context.setVariable(parameterNames[i], args[i]);
            }
        }

        // 创建缓存key（添加冒号分隔符）
        String key = keyPrefix + ":" + expression.getValue(context);

        try {
            // 从缓存里面查询
            Object cache = redisTemplate.opsForValue().get(key);

            // 判断缓存是否存在
            if (cache != null) {
                log.debug("Cache hit for key: {}", key);
                return cache;
            }

            // 执行方法查询数据
            Object result = joinPoint.proceed();

            // 处理缓存穿透：空值也缓存
            if (result == null) {
                if (isCacheNull) {
                    // 缓存空值防止穿透，设置较短过期时间
                    redisTemplate.opsForValue().set(key, "NULL_VALUE", 5, TimeUnit.MINUTES);
                    log.debug("Cached null value for key: {}", key);
                }
                return null;
            }

            // 缓存结果
            redisTemplate.opsForValue().set(key, result, expire, timeUnit);
            log.debug("Cached result for key: {}", key);

            return result;

        } catch (Exception ex) {
            // 缓存操作异常处理，不影响业务逻辑
            log.error("Cache operation failed for key: {}", key, ex);
            return joinPoint.proceed();
        }
    }

}
